<!DOCTYPE html>
<html>
<head>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    </style>
    <style>
        /* Ensures the image stays in the top-right corner */
        .top-right {
            position: absolute;
            top: 40px;  /* Distance from top of the screen */
            right: 40px; /* Distance from right of the screen */
            z-index: 100; /* Ensures it stays above other elements */
        }
    </style>
    <meta charset="UTF-8">
</head>
<body>
    <script src="./3rd/three.min.js"></script>
    <script src="./3rd/geotiff.js"></script>
    <script src="./3rd/d3.v7.min.js"></script>
    <script src="./3rd/OrbitControls.js"></script>

    <!-- <div style='position:fixed; z-index:999; top:0; padding: 10'>
        夸张倍数： <input type="text" id="haha" value="100000">
        <input type="button" value="确定" onclick="update()" />
        <input type="button" value="切换显示" onclick="changeDisplayMode()" />
    </div> -->

    <img src="./assets/compass.png" alt="Image" class="top-right" id="compass" width="120">
    <script>
        // 全局变量
        let scene, camera, renderer, waterMesh, waterTiffData, terrainTiffData, terrainMesh, sideMesh, bottomMesh, sideMesh2;
        let textLabels = [];

        let exaggerationScale = 100000;
        let width, height;
        let edges = [];  
        let arrowHelperGroup;
        let enableShowTextLabel = true;
        let contoursLines = [];

        let showTerrain = false;
        let sideMeshes = [];
        let sideMeshes2 = [];

        let minDepth = Number.MAX_VALUE;
        let maxDepth = Number.MIN_VALUE;

        function rotateImage() {
            const cameraDirection = new THREE.Vector3();
            camera.getWorldDirection(cameraDirection);

            const angle = Math.atan2(cameraDirection.x, cameraDirection.z);
            document.getElementById('compass').style.transform = 'rotate(' + angle + 'rad)';
        }

        function clearScene() {

            if (waterMesh) {
                scene.remove(waterMesh);
                waterMesh = undefined;
            }
            
            if (sideMesh) {
                scene.remove(sideMesh);
                sideMesh = undefined;
            }

            if (sideMesh2) {
                scene.remove(sideMesh2);
                sideMesh2 = undefined;
            }

            if (bottomMesh) {
                scene.remove(bottomMesh);
                bottomMesh = undefined;
            }

            if (terrainMesh) {
                scene.remove(terrainMesh);
                terrainMesh = undefined;
            }

            if (arrowHelperGroup) {
                scene.remove(arrowHelperGroup);
                arrowHelperGroup = undefined;
            }

            if (contoursLines.length > 0) {
                contoursLines.forEach(line => {
                    scene.remove(line);
                })
                contoursLines = [];
            }

            if (textLabels.length > 0) {
                textLabels.forEach(text => {
                    scene.remove(text);
                })
                textLabels = [];
            }

            if (sideMeshes.length > 0) {
                sideMeshes.forEach(mesh => {
                    scene.remove(mesh);
                })
                sideMeshes = [];
            }        

            if (sideMeshes2.length > 0) {
                sideMeshes2.forEach(mesh => {
                    scene.remove(mesh);
                })
                sideMeshes2 = [];
            }   
        }

        // 初始化Three.js场景
        function init() {
            scene = new THREE.Scene();
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.3, 1000);
            camera.position.set(1, 154, -142);

            renderer = new THREE.WebGLRenderer();
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement);

            // 添加OrbitControls
            controls = new THREE.OrbitControls(camera, renderer.domElement);
            controls.enableDamping = true; // 启用阻尼（惯性）
            controls.dampingFactor = 0.25;
            controls.screenSpacePanning = false;

            //loadTiff('./assets/西辽河2023-07月水位埋深_bak.tif');

            // 窗口大小改变时更新渲染器和相机
            window.addEventListener('resize', onWindowResize, false);

            animate();
        }

        // 加载和解析tif文件
        async function loadTiff(blob) {
            clearScene();
            camera.position.set(1, 154, -142);

            const url = URL.createObjectURL(blob);
            let response = await fetch(url);
            let arrayBuffer = await response.arrayBuffer();
            let tiff = await GeoTIFF.fromArrayBuffer(arrayBuffer);
            let image = await tiff.getImage();
            let rasters = await image.readRasters();
            waterTiffData = rasters[0];  // 假设只有一个通道

            width = image.getWidth();
            height = image.getHeight();

            // response = await fetch(waterTiffUrl);
            // arrayBuffer = await response.arrayBuffer();
            // tiff = await GeoTIFF.fromArrayBuffer(arrayBuffer);
            // image = await tiff.getImage();
            // rasters = await image.readRasters();
            // terrainTiffData = rasters[0];  // 假设只有一个通道

            getEdges(waterTiffData);

            createWater();
            createTerrain();
            createBottom();

            if (edges.length > 0) {
                createNewSide(waterTiffData);
                createNewSide2(waterTiffData);
            } else {
                createSide();
                createSide2();
            }
        }

        function getEdges(tiffData) {
            // 检测并绘制轮廓线
            edges = marchingSquaresWithHeight(tiffData);
              // 对轮廓线点进行排序
            edges = findConnectedContour(edges);
            // 去除排序后的重复顶点
            edges = removeDuplicateVertices(edges);
        }

        function createUpWaterSideMaterial() {
            // 创建纹理加载器
            const textureLoader = new THREE.TextureLoader();
            const textures = [
                textureLoader.load('./assets/GeoTexture/1.jpg'),
                textureLoader.load('./assets/GeoTexture/2.jpg'),
                textureLoader.load('./assets/GeoTexture/3.jpg'),
                textureLoader.load('./assets/GeoTexture/4.jpg')
            ];

            textures.forEach(t => {
                t.wrapS = t.wrapT = THREE.RepeatWrapping;
                //t.repeat.x = t.repeat.y = 10;
            });

            const repeatU = 500.0

            // 创建自定义材质，利用ShaderMaterial实现多纹理混合
            const material = new THREE.ShaderMaterial({
                side: THREE.DoubleSide,
                transparent: false,
                uniforms: {
                    texture1: { value: textures[0] },
                    texture2: { value: textures[1] },
                    texture3: { value: textures[2] },
                    texture4: { value: textures[3] },
                    count: { value: textures.length },
                    repeatU: { value: repeatU }
                },
                vertexShader: `
                    varying vec2 vUv;
                    void main() {
                        vUv = uv;
                        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
                    }
                `,
                fragmentShader: `
                    uniform sampler2D texture1;
                    uniform sampler2D texture2;
                    uniform sampler2D texture3;
                    uniform sampler2D texture4;
                    uniform int count;
                    uniform float repeatU;
                    varying vec2 vUv;

                    void main() {
                        float segmentHeight = 1.0 / float(count);
                        vec4 color = vec4(0.0);
                        
                        if (vUv.y < segmentHeight * 0.6) {
                            color = texture2D(texture1, vec2(vUv.x * repeatU, vUv.y / segmentHeight));
                        } else if (vUv.y < segmentHeight * 2.0) {
                            color = texture2D(texture2, vec2(vUv.x * repeatU, (vUv.y - segmentHeight) / segmentHeight));
                        } else if (vUv.y < segmentHeight * 3.3) {
                            color = texture2D(texture3, vec2(vUv.x * repeatU, (vUv.y - segmentHeight * 2.5) / segmentHeight));
                        } else {
                            color = texture2D(texture4, vec2(vUv.x * repeatU, (vUv.y - segmentHeight * 3.0) / segmentHeight));
                        }

                        color.a = 0.7;
                        gl_FragColor = color;
                    }
                 `
            });

            return material;
        }

        function createDownWaterSideMaterial() {
            // 创建纹理加载器
            const textureLoader = new THREE.TextureLoader();
            const textures = [
                textureLoader.load('./assets/GeoTexture/水.bmp'),
                textureLoader.load('./assets/GeoTexture/6.jpg')
            ];

            textures.forEach(t => {
                t.wrapS = t.wrapT = THREE.RepeatWrapping;
                //t.repeat.x = t.repeat.y = 10;
            });

            const repeatU = 500.0

            // 创建自定义材质，利用ShaderMaterial实现多纹理混合
            const material = new THREE.ShaderMaterial({
                side: THREE.DoubleSide,
                transparent: false,
                uniforms: {
                    texture1: { value: textures[0] },
                    texture2: { value: textures[1] },
                    count: { value: textures.length },
                    repeatU: { value: repeatU }
                },
                vertexShader: `
                    varying vec2 vUv;
                    void main() {
                        vUv = uv;
                        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
                    }
                `,
                fragmentShader: `
                    uniform sampler2D texture1;
                    uniform sampler2D texture2;
                    uniform int count;
                    uniform float repeatU;
                    varying vec2 vUv;

                    void main() {
                        float segmentHeight = 1.0 / float(count);
                        vec4 color = vec4(0.0);
                        
                        if (vUv.y < segmentHeight * 1.2) {
                            color = texture2D(texture1, vec2(vUv.x * repeatU, vUv.y / segmentHeight));
                        } else {
                            color = texture2D(texture2, vec2(vUv.x * repeatU, vUv.y / segmentHeight));
                        }

                        gl_FragColor = color;
                    }
                 `
            });

            return material;
        }

        function createWater() {
            let min = Number.MAX_VALUE;
            let max = Number.MIN_VALUE;
            const values = [];

            for (let n = 0; n < height; ++n) {
                values.push([]);
            }

            const geometry = new THREE.PlaneGeometry(width, height, width - 1, height - 1);
            //console.log(geometry.attributes.position);
            for (let i = 0, j = 0; i < waterTiffData.length; i++, j += 3) {
                const value = waterTiffData[i] * (-1.0); // 65535 * exaggerationScale * (-1.0);
                geometry.attributes.position.array[j + 2] = value;

                const m = parseInt(i / width);
                values[m].push(value);

                if (value > max) {
                    max = value;
                }

                if (value < min) {
                    min = value;
                }

                if (waterTiffData[i] > maxDepth) {
                    maxDepth = waterTiffData[i];
                    window.maxDepth = maxDepth;
                }
                if (waterTiffData[i] < minDepth) {
                    minDepth = waterTiffData[i];
                    window.minDepth = minDepth;
                }
            }
            geometry.computeVertexNormals();

            createContour(values, width, height, min, max);

            let terrainUniforms = {
                min: {value: min},
                max: {value: max},
                showPositionColors: {value: true},
                lineThickness: {value: 1}
            }

            let m = new THREE.MeshLambertMaterial({
                color: 0x7D6747,
                wireframe: false,
                side: THREE.DoubleSide,
                onBeforeCompile: shader => {
                    shader.uniforms.boxMin = terrainUniforms.min;
                    shader.uniforms.boxMax = terrainUniforms.max;
                    shader.uniforms.lineThickness = terrainUniforms.lineThickness;
                    shader.uniforms.showPositionColors = terrainUniforms.showPositionColors;
                    shader.vertexShader = `
                        varying vec3 vPos;
                    ${shader.vertexShader}
                    `.replace(
                        `#include <begin_vertex>`,
                    `#include <begin_vertex>
                        vPos = transformed;
                    `
                    );
                //console.log(shader.vertexShader);
                shader.fragmentShader = `
                uniform float boxMin;
                uniform float boxMax;
                uniform float showPositionColors;
                uniform float lineThickness;
                varying vec3 vPos;
                ${shader.fragmentShader}
                `.replace(
                `#include <dithering_fragment>`,
                `
                    vec3 col = vec3(0);
                    float ratio = (vPos.z - boxMin) / (boxMax - boxMin);
                    col = mix(vec3(0, 0, 0.5), vec3(0, 0.6, 1), ratio);

                    // float coord = vPos.z * 1.;
                    // float grid = abs(fract(coord - 0.5) - 0.5) / fwidth(coord) / lineThickness;
                    // float line = min(grid, 1.0);
                    // vec3 lineCol = mix(vec3(1, 1, 0),vec3(0, 1, 1), col.z);
                    // col = mix(lineCol, col, line);

                    gl_FragColor = vec4(col, opacity);
                `
                );
                //console.log(shader.fragmentShader);
            }
        });

            m.defines = {"USE_UV":""};
            m.extensions = {derivatives: true};


            waterMesh = new THREE.Mesh(geometry, m);
            waterMesh.rotateX(Math.PI * -0.5);
            //waterMesh.position.y = 5;
            scene.add(waterMesh);
            waterMesh.visible = true;

            arrowHelperGroup = new THREE.Group();

            for (let i = 0; i < geometry.attributes.position.count; i += 100) { // 可以减少箭头数量以提高性能
                const pos = new THREE.Vector3(
                    geometry.attributes.position.getX(i),
                    geometry.attributes.position.getY(i),
                    geometry.attributes.position.getZ(i)
                );

                const normal = new THREE.Vector3(
                    geometry.attributes.normal.getX(i),
                    geometry.attributes.normal.getY(i),
                    0
                );

                // 创建箭头辅助器
                const arrowHelper = new THREE.ArrowHelper(normal.clone().normalize(), pos, 4, 0xff0000, 2, 1);

                arrowHelperGroup.add(arrowHelper);
            }

            arrowHelperGroup.rotateX(Math.PI * -0.5);
            scene.add(arrowHelperGroup);
            arrowHelperGroup.visible = !showTerrain;
        }

        function createSide() {

            const material = createUpWaterSideMaterial();

            let sideGeometry = new THREE.BufferGeometry();
            let sideVertices = [];
            let indices = [];
            let uvs = [];

            const bottomVertices = waterMesh.geometry.attributes.position.array;
            const topVertices = waterMesh.geometry.attributes.position.array;

            // 创建第一个面
            for (let i = 0; i < width; ++i) {
                // 顶边的顶点
                sideVertices.push(bottomVertices[i * 3], bottomVertices[i * 3 + 1], bottomVertices[i * 3 + 2] + 20);
                // 底边的顶点
                sideVertices.push(bottomVertices[i * 3], bottomVertices[i * 3 + 1], bottomVertices[i * 3 + 2]);
            }

            for (let i = 0; i < width * 2 - 2; i += 2) {
                indices.push(i);
                indices.push(i + 1);
                indices.push(i + 2);

                indices.push(i + 1);
                indices.push(i + 3);
                indices.push(i + 2);
            }

            for (let i = 0; i < width; ++i) {
                uvs.push(i / (width - 1));
                uvs.push(0);

                uvs.push(i / (width - 1));
                uvs.push(1);
            }

            sideGeometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideGeometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2));
            sideGeometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1));

            let sideMesh = new THREE.Mesh(sideGeometry, material);
            sideMesh.rotateX(Math.PI * -0.5);
            scene.add(sideMesh);
            sideMeshes.push(sideMesh);

            // 创建第二个面
            sideGeometry = new THREE.BufferGeometry();
            sideVertices = [];
            let start = topVertices.length - width * 3
            for (let i = 0; i < width; ++i) {
                // 顶边的顶点
                sideVertices.push(bottomVertices[start + i * 3], bottomVertices[start + i * 3 + 1], bottomVertices[start + i * 3 + 2] + 20);
                // 底边的顶点
                sideVertices.push(bottomVertices[start + i * 3], bottomVertices[start + i * 3 + 1], bottomVertices[start + i * 3 + 2]);
            }

            sideGeometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideGeometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2));
            sideGeometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1));

            sideMesh = new THREE.Mesh(sideGeometry, material);
            sideMesh.rotateX(Math.PI * -0.5);
            scene.add(sideMesh);
            sideMeshes.push(sideMesh);

            // 创建第三个面
            sideGeometry = new THREE.BufferGeometry();
            sideVertices = [];
            indices = [];
            uvs = [];

            for (let i = 0; i < height; ++i) {
                
                start = width * i * 3

                // 顶边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2] + 20);
                // 底边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2]);
            }

            for (let i = 0; i < height * 2 - 2; i += 2) {
                indices.push(i);
                indices.push(i + 1);
                indices.push(i + 2);

                indices.push(i + 1);
                indices.push(i + 3);
                indices.push(i + 2);
            }

            for (let i = 0; i < height; ++i) {
                uvs.push(i / (width - 1));
                uvs.push(0);

                uvs.push(i / (width - 1));
                uvs.push(1);
            }

            sideGeometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideGeometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2));
            sideGeometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1));

            sideMesh = new THREE.Mesh(sideGeometry, material);
            sideMesh.rotateX(Math.PI * -0.5);
            scene.add(sideMesh);
            sideMeshes.push(sideMesh);

            // 创建第四个面
            sideGeometry = new THREE.BufferGeometry();
            sideVertices = [];

            for (let i = 0; i < height; ++i) {
                
                start = width * (i + 1) * 3 - 3 

                // 顶边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2] + 20);
                // 底边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2]);
            }

            sideGeometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideGeometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2));
            sideGeometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1));

            sideMesh = new THREE.Mesh(sideGeometry, material);
            sideMesh.rotateX(Math.PI * -0.5);
            scene.add(sideMesh);
            sideMeshes.push(sideMesh);

            sideMeshes.forEach(mesh => {
                mesh.visible = showTerrain;
            });
        }

        function updateSide() {
            if (sideMeshes.length === 0) {
                return;
            }

            let sideVertices = [];


            const bottomVertices = waterMesh.geometry.attributes.position.array;
            const topVertices = waterMesh.geometry.attributes.position.array;

            // 创建第一个面
            for (let i = 0; i < width; ++i) {
                // 顶边的顶点
                sideVertices.push(bottomVertices[i * 3], bottomVertices[i * 3 + 1], bottomVertices[i * 3 + 2] + 20);
                // 底边的顶点
                sideVertices.push(bottomVertices[i * 3], bottomVertices[i * 3 + 1], bottomVertices[i * 3 + 2]);
            }

            sideMeshes[0].geometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideMeshes[0].geometry.attributes.position.needsUpdate = true;

            


            // 创建第二个面
            sideVertices = [];
            let start = topVertices.length - width * 3
            for (let i = 0; i < width; ++i) {
                // 顶边的顶点
                sideVertices.push(bottomVertices[start + i * 3], bottomVertices[start + i * 3 + 1], bottomVertices[start + i * 3 + 2] + 20);
                // 底边的顶点
                sideVertices.push(bottomVertices[start + i * 3], bottomVertices[start + i * 3 + 1], bottomVertices[start + i * 3 + 2]);
            }

            sideMeshes[1].geometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideMeshes[1].geometry.attributes.position.needsUpdate = true;

            // 创建第三个面
            sideVertices = [];
            for (let i = 0; i < height; ++i) {
                
                start = width * i * 3

                // 顶边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2] + 20);
                // 底边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2]);
            }

            sideMeshes[2].geometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideMeshes[2].geometry.attributes.position.needsUpdate = true;

            // 创建第四个面
            sideVertices = [];
            for (let i = 0; i < height; ++i) {
                
                start = width * (i + 1) * 3 - 3 

                // 顶边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2] + 20);
                // 底边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2]);
            }

            sideMeshes[3].geometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideMeshes[3].geometry.attributes.position.needsUpdate = true;
        }

        function createSide2() {

            const material = createDownWaterSideMaterial();

            let sideGeometry = new THREE.BufferGeometry();
            let sideVertices = [];
            let indices = [];
            let uvs = [];

            const bottomVertices = waterMesh.geometry.attributes.position.array;
            const topVertices = waterMesh.geometry.attributes.position.array;

            // 创建第一个面
            for (let i = 0; i < width; ++i) {
                // 顶边的顶点
                sideVertices.push(bottomVertices[i * 3], bottomVertices[i * 3 + 1], bottomVertices[i * 3 + 2]);
                // 底边的顶点
                sideVertices.push(bottomVertices[i * 3], bottomVertices[i * 3 + 1], bottomVertices[i * 3 + 2] - 5);
            }

            for (let i = 0; i < width * 2 - 2; i += 2) {
                indices.push(i);
                indices.push(i + 1);
                indices.push(i + 2);

                indices.push(i + 1);
                indices.push(i + 3);
                indices.push(i + 2);
            }

            for (let i = 0; i < width; ++i) {
                uvs.push(i / (width - 1));
                uvs.push(0);

                uvs.push(i / (width - 1));
                uvs.push(1);
            }

            sideGeometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideGeometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2));
            sideGeometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1));

            let sideMesh = new THREE.Mesh(sideGeometry, material);
            sideMesh.rotateX(Math.PI * -0.5);
            scene.add(sideMesh);
            sideMeshes2.push(sideMesh);

            // 创建第二个面
            sideGeometry = new THREE.BufferGeometry();
            sideVertices = [];
            let start = topVertices.length - width * 3
            for (let i = 0; i < width; ++i) {
                // 顶边的顶点
                sideVertices.push(bottomVertices[start + i * 3], bottomVertices[start + i * 3 + 1], bottomVertices[start + i * 3 + 2]);
                // 底边的顶点
                sideVertices.push(bottomVertices[start + i * 3], bottomVertices[start + i * 3 + 1], bottomVertices[start + i * 3 + 2] - 5);
            }

            sideGeometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideGeometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2));
            sideGeometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1));

            sideMesh = new THREE.Mesh(sideGeometry, material);
            sideMesh.rotateX(Math.PI * -0.5);
            scene.add(sideMesh);
            sideMeshes2.push(sideMesh);

            // 创建第三个面
            sideGeometry = new THREE.BufferGeometry();
            sideVertices = [];
            indices = [];
            uvs = [];

            for (let i = 0; i < height; ++i) {
                
                start = width * i * 3

                // 顶边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2]);
                // 底边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2] - 5);
            }

            for (let i = 0; i < height * 2 - 2; i += 2) {
                indices.push(i);
                indices.push(i + 1);
                indices.push(i + 2);

                indices.push(i + 1);
                indices.push(i + 3);
                indices.push(i + 2);
            }

            for (let i = 0; i < height; ++i) {
                uvs.push(i / (width - 1));
                uvs.push(0);

                uvs.push(i / (width - 1));
                uvs.push(1);
            }

            sideGeometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideGeometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2));
            sideGeometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1));

            sideMesh = new THREE.Mesh(sideGeometry, material);
            sideMesh.rotateX(Math.PI * -0.5);
            scene.add(sideMesh);
            sideMeshes2.push(sideMesh);

            // 创建第四个面
            sideGeometry = new THREE.BufferGeometry();
            sideVertices = [];

            for (let i = 0; i < height; ++i) {
                
                start = width * (i + 1) * 3 - 3 

                // 顶边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2]);
                // 底边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2] - 5);
            }

            sideGeometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideGeometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2));
            sideGeometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1));

            sideMesh = new THREE.Mesh(sideGeometry, material);
            sideMesh.rotateX(Math.PI * -0.5);
            scene.add(sideMesh);
            sideMeshes2.push(sideMesh);
        }

        function updateSide2() {
            if (sideMeshes2.length === 0) {
                return;
            }

            let sideVertices = [];

            const bottomVertices = waterMesh.geometry.attributes.position.array;
            const topVertices = waterMesh.geometry.attributes.position.array;

            // 创建第一个面
            for (let i = 0; i < width; ++i) {
                // 顶边的顶点
                sideVertices.push(bottomVertices[i * 3], bottomVertices[i * 3 + 1], bottomVertices[i * 3 + 2]);
                // 底边的顶点
                sideVertices.push(bottomVertices[i * 3], bottomVertices[i * 3 + 1], bottomVertices[i * 3 + 2] - 5);
            }

            sideMeshes2[0].geometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideMeshes2[0].geometry.attributes.position.needsUpdate = true;

            // 创建第二个面
            sideVertices = [];
            let start = topVertices.length - width * 3
            for (let i = 0; i < width; ++i) {
                // 顶边的顶点
                sideVertices.push(bottomVertices[start + i * 3], bottomVertices[start + i * 3 + 1], bottomVertices[start + i * 3 + 2]);
                // 底边的顶点
                sideVertices.push(bottomVertices[start + i * 3], bottomVertices[start + i * 3 + 1], bottomVertices[start + i * 3 + 2] - 5);
            }

            sideMeshes2[1].geometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideMeshes2[1].geometry.attributes.position.needsUpdate = true;

            // 创建第三个面
            sideVertices = [];
            for (let i = 0; i < height; ++i) {
                
                start = width * i * 3

                // 顶边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2]);
                // 底边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2] - 5);
            }

            sideMeshes2[2].geometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideMeshes2[2].geometry.attributes.position.needsUpdate = true;

            // 创建第四个面
            sideVertices = [];
            for (let i = 0; i < height; ++i) {
                
                start = width * (i + 1) * 3 - 3 

                // 顶边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2]);
                // 底边的顶点
                sideVertices.push(bottomVertices[start], bottomVertices[start + 1], bottomVertices[start + 2] - 5);
            }

            sideMeshes2[3].geometry.setAttribute('position', new THREE.Float32BufferAttribute(sideVertices, 3));
            sideMeshes2[3].geometry.attributes.position.needsUpdate = true;
        }


        function createNewSide(tiffData) {
            let sideVertices = [];   
            let sidePositinAttribute;

            // const edgeGeometry = createOutLines(edges);
            // const edgeMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 });
            // const edgeLines = new THREE.LineSegments(edgeGeometry, edgeMaterial);
            // edgeLines.rotateX(Math.PI * -0.5);
            //scene.add(edgeLines);

            //console.log(edges);
            const edgeVertexs= createOutlineVertexs(edges);
            const sideMaterial = createUpWaterSideMaterial();

            let sideGeometry = new THREE.BufferGeometry();
            const vertexsCount = edgeVertexs.length / 3;
            
            let indices = [];
            let uvs = [];

            //console.log(edgeVertexs);

            for (let i = 0; i < vertexsCount; ++i) {
                // 顶边的顶点
                sideVertices.push(edgeVertexs[i * 3], edgeVertexs[i * 3 + 1], edgeVertexs[i * 3 + 2] + 20);
                // 底边的顶点
                sideVertices.push(edgeVertexs[i * 3], edgeVertexs[i * 3 + 1], edgeVertexs[i * 3 + 2]);
            }

            sideVertices.push(edgeVertexs[0], edgeVertexs[1], edgeVertexs[2] + 20);
            sideVertices.push(edgeVertexs[0], edgeVertexs[1], edgeVertexs[2]);

            for (let i = 0; i < vertexsCount * 2; i += 2) {
                indices.push(i);
                indices.push(i + 1);
                indices.push(i + 2);

                indices.push(i + 1);
                indices.push(i + 3);
                indices.push(i + 2);
            }

            for (let i = 0; i <= vertexsCount; ++i) {
                uvs.push(i / (vertexsCount - 1));
                uvs.push(0);

                uvs.push(i / (vertexsCount - 1));
                uvs.push(1);
            }

            sidePositinAttribute = new THREE.Float32BufferAttribute(sideVertices, 3);
            //sidePositinAttribute.setUsage(THREE.DynamicDrawUsage);
            sideGeometry.setAttribute('position', sidePositinAttribute);
            sideGeometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2));
            sideGeometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1));

            sideMesh = new THREE.Mesh(sideGeometry, sideMaterial);
            sideMesh.visible = showTerrain;
            sideMesh.rotateX(Math.PI * -0.5);
            scene.add(sideMesh);
        }

        function createNewSide2(tiffData) {
            let sideVertices = [];
            let edges = [];     
            let sidePositinAttribute;

            // 检测并绘制轮廓线
            edges = marchingSquaresWithHeight(tiffData);
            // 对轮廓线点进行排序
            edges = findConnectedContour(edges);
            // 去除排序后的重复顶点
            edges = removeDuplicateVertices(edges);

            //scene.add(edgeLines);

            const edgeVertexs= createOutlineVertexs(edges);
            const sideMaterial = createDownWaterSideMaterial();

            let sideGeometry = new THREE.BufferGeometry();
            const vertexsCount = edgeVertexs.length / 3;
            
            let indices = [];
            let uvs = [];

            //console.log(edgeVertexs);

            for (let i = 0; i < vertexsCount; ++i) {
                // 顶边的顶点
                sideVertices.push(edgeVertexs[i * 3], edgeVertexs[i * 3 + 1], edgeVertexs[i * 3 + 2]);
                // 底边的顶点
                sideVertices.push(edgeVertexs[i * 3], edgeVertexs[i * 3 + 1], edgeVertexs[i * 3 + 2] - 5);
            }

            sideVertices.push(edgeVertexs[0], edgeVertexs[1], edgeVertexs[2]);
            sideVertices.push(edgeVertexs[0], edgeVertexs[1], edgeVertexs[2] - 5);

            for (let i = 0; i < vertexsCount * 2; i += 2) {
                indices.push(i);
                indices.push(i + 1);
                indices.push(i + 2);

                indices.push(i + 1);
                indices.push(i + 3);
                indices.push(i + 2);
            }

            for (let i = 0; i <= vertexsCount; ++i) {
                uvs.push(i / (vertexsCount - 1));
                uvs.push(0);

                uvs.push(i / (vertexsCount - 1));
                uvs.push(1);
            }

            sidePositinAttribute = new THREE.Float32BufferAttribute(sideVertices, 3);
            sideGeometry.setAttribute('position', sidePositinAttribute);
            sideGeometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2));
            sideGeometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1));

            sideMesh2 = new THREE.Mesh(sideGeometry, sideMaterial);
            sideMesh2.rotateX(Math.PI * -0.5);
            scene.add(sideMesh2);
        }

        function updateNewSide() {
            if (!sideMesh) {
                return;
            }

            const edgeVertexs= createOutlineVertexs(edges);
            const vertexsCount = edgeVertexs.length / 3;
            const newSideVertices = [];
            for (let i = 0; i < vertexsCount; ++i) {
                // 顶边的顶点
                newSideVertices.push(edgeVertexs[i * 3], edgeVertexs[i * 3 + 1], edgeVertexs[i * 3 + 2] + 20);
                // 底边的顶点
                newSideVertices.push(edgeVertexs[i * 3], edgeVertexs[i * 3 + 1], edgeVertexs[i * 3 + 2]);
            }

            newSideVertices.push(edgeVertexs[0], edgeVertexs[1], edgeVertexs[2] + 20);
            newSideVertices.push(edgeVertexs[0], edgeVertexs[1], edgeVertexs[2]);

            const newSidePositinAttribute = new THREE.Float32BufferAttribute(newSideVertices, 3);
            sideMesh.geometry.setAttribute('position', newSidePositinAttribute);

            sideMesh.geometry.attributes.position.needsUpdate = true;
        }

        function updateNewSide2() {
            if (!sideMesh2) {
                return;
            }

            const edgeVertexs= createOutlineVertexs(edges);
            const vertexsCount = edgeVertexs.length / 3;
            const newSideVertices = [];
            for (let i = 0; i < vertexsCount; ++i) {
                // 顶边的顶点
                newSideVertices.push(edgeVertexs[i * 3], edgeVertexs[i * 3 + 1], edgeVertexs[i * 3 + 2]);
                // 底边的顶点
                newSideVertices.push(edgeVertexs[i * 3], edgeVertexs[i * 3 + 1], edgeVertexs[i * 3 + 2] - 5);
            }

            newSideVertices.push(edgeVertexs[0], edgeVertexs[1], edgeVertexs[2]);
            newSideVertices.push(edgeVertexs[0], edgeVertexs[1], edgeVertexs[2] - 5);

            const newSidePositinAttribute = new THREE.Float32BufferAttribute(newSideVertices, 3);
            sideMesh2.geometry.setAttribute('position', newSidePositinAttribute);

            sideMesh2.geometry.attributes.position.needsUpdate = true;
        }

        function findConnectedContour(contours) {
            const sortedContours = [];
            const visited = new Set();

            const findNearest = (point, remainingPoints) => {
                let nearestIndex = -1;
                let minDistance = Infinity;

                for (let i = 0; i < remainingPoints.length; i++) {
                const [x, y, z] = remainingPoints[i];
                const dx = point[0] - x;
                const dy = point[1] - y;
                const dz = point[2] - z;
                const distance = dx * dx + dy * dy + dz * dz;

                if (distance < minDistance) {
                    minDistance = distance;
                    nearestIndex = i;
                }
                }

                return nearestIndex;
            };

            while (contours.length > 0) {
                const startPoint = contours.shift();
                const currentLine = [startPoint];
                visited.add(startPoint.join(','));

                let currentPoint = startPoint;
                while (true) {
                const remainingPoints = contours.filter(
                    point => !visited.has(point.join(','))
                );
                if (remainingPoints.length === 0) break;

                const nearestIndex = findNearest(currentPoint, remainingPoints);
                if (nearestIndex === -1) break;

                const nearestPoint = remainingPoints[nearestIndex];
                currentLine.push(nearestPoint);
                visited.add(nearestPoint.join(','));

                // Remove the point from the original list
                contours = contours.filter(
                    point => point.join(',') !== nearestPoint.join(',')
                );

                currentPoint = nearestPoint;
                }

                sortedContours.push(...currentLine);
            }

            return sortedContours;
        }

        function removeDuplicateVertices(contours) {
            const uniqueVertices = new Set();
            const uniqueContours = [];

            contours.forEach(([x, y, z]) => {
                const key = `${x},${y},${z}`;
                if (!uniqueVertices.has(key)) {
                uniqueVertices.add(key);
                uniqueContours.push([x, y, z]);
                }
            });

            return uniqueContours;
        }

        function getSafeZValue(x, y, data) {
            const z = data[y * width + x];
            if (!isNaN(z)) return z;

            // 找到附近的非 NaN 高度
            const neighbors = [
                [x - 1, y], [x + 1, y],
                [x, y - 1], [x, y + 1],
                [x - 1, y - 1], [x + 1, y + 1],
                [x - 1, y + 1], [x + 1, y - 1]
            ];

            let sum = 0, count = 0;
            for (const [nx, ny] of neighbors) {
                if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
                const neighborZ = data[ny * width + nx];
                if (!isNaN(neighborZ)) {
                    sum += neighborZ;
                    count++;
                }
                }
            }

            return count > 0 ? sum / count : 0;  // 返回邻近点的平均高度，或者0作为默认值
        }

        function marchingSquaresWithHeight(data) {
            const contours = [];

            const getValue = (x, y) => {
                if (x < 0 || x >= width || y < 0 || y >= height) return 1;
                return isNaN(data[y * width + x]) ? 1 : 0;
            };

            for (let y = 0; y < height - 1; y++) {
                for (let x = 0; x < width - 1; x++) {
                const squareIndex = 
                    (getValue(x, y) << 3) |
                    (getValue(x + 1, y) << 2) |
                    (getValue(x + 1, y + 1) << 1) |
                    (getValue(x, y + 1) << 0);

                const addVertex = (xPos, yPos) => {
                    const zPos = getSafeZValue(Math.floor(xPos), Math.floor(yPos), data);
                    contours.push([xPos, yPos, zPos]);
                };

                switch (squareIndex) {
                    case 1:
                    case 14:
                    addVertex(x, y + 0.5);
                    addVertex(x + 0.5, y + 1);
                    break;
                    case 2:
                    case 13:
                    addVertex(x + 0.5, y + 1);
                    addVertex(x + 1, y + 0.5);
                    break;
                    case 3:
                    case 12:
                    addVertex(x, y + 0.5);
                    addVertex(x + 1, y + 0.5);
                    break;
                    case 4:
                    case 11:
                    addVertex(x + 1, y + 0.5);
                    addVertex(x + 0.5, y);
                    break;
                    case 5:
                    addVertex(x, y + 0.5);
                    addVertex(x + 0.5, y);
                    addVertex(x + 0.5, y + 1);
                    addVertex(x + 1, y + 0.5);
                    break;
                    case 6:
                    case 9:
                    addVertex(x + 0.5, y);
                    addVertex(x + 0.5, y + 1);
                    break;
                    case 7:
                    case 8:
                    addVertex(x, y + 0.5);
                    addVertex(x + 0.5, y);
                    break;
                    case 10:
                    addVertex(x + 0.5, y);
                    addVertex(x + 1, y + 0.5);
                    addVertex(x, y + 0.5);
                    addVertex(x + 0.5, y + 1);
                    break;
                }
                }
            }

            return contours;
        }

        function createOutLines(contours) {
            const geometry = new THREE.BufferGeometry();
            const positions = [];

            contours.forEach(([x, y, z]) => {
                positions.push(
                    x - width / 2.0, // Normalize X
                    -(y - height / 2.0), // Normalize Y and invert
                    z / 65535 * 10000 * (-1.0)
                );
            });

            geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
            return geometry;
        }

        function createOutlineVertexs(contours) {
            const geometry = new THREE.BufferGeometry();
            const positions = [];

            contours.forEach(([x, y, z]) => {
                positions.push(
                    x - width / 2.0, // Normalize X
                    -(y - height / 2.0), // Normalize Y and invert
                    z * -1 // / 65535 * exaggerationScale * (-1.0)
                );
            });

            return positions;
        }

        function updateWater() {
            let min = Number.MAX_VALUE;
            let max = Number.MIN_VALUE;

            const values = [];
            for (let n = 0; n < height; ++n) {
                values.push([]);
            }

            for (let i = 0, j = 0; i < waterTiffData.length; i++, j += 3) {
                const value = waterTiffData[i] * -1; // / 65535 * exaggerationScale * (-1.0);
                waterMesh.geometry.attributes.position.array[j + 2] = value;
                waterMesh.geometry.attributes.position.needsUpdate = true;

                const m = parseInt(i / width);
                values[m].push(value);

                if (value > max) {
                    max = value;
                }

                if (value < min) {
                    min = value;
                }
            }

            waterMesh.geometry.computeVertexNormals();

            createContour(values, width, height, min, max);

            let terrainUniforms = {
                min: {value: min},
                max: {value: max},
                showPositionColors: {value: true},
                lineThickness: {value: 1}
            }

            let m = new THREE.MeshLambertMaterial({
                color: 0x7D6747,
                wireframe: false,
                side: THREE.DoubleSide,
                onBeforeCompile: shader => {
                    shader.uniforms.boxMin = terrainUniforms.min;
                    shader.uniforms.boxMax = terrainUniforms.max;
                    shader.uniforms.lineThickness = terrainUniforms.lineThickness;
                    shader.uniforms.showPositionColors = terrainUniforms.showPositionColors;
                    shader.vertexShader = `
                        varying vec3 vPos;
                    ${shader.vertexShader}
                    `.replace(
                        `#include <begin_vertex>`,
                    `#include <begin_vertex>
                        vPos = transformed;
                    `
                    );
                //console.log(shader.vertexShader);
                shader.fragmentShader = `
                uniform float boxMin;
                uniform float boxMax;
                uniform float showPositionColors;
                uniform float lineThickness;
                varying vec3 vPos;
                ${shader.fragmentShader}
                `.replace(
                `#include <dithering_fragment>`,
                `
                    vec3 col = vec3(0);
                    float ratio = (vPos.z - boxMin) / (boxMax - boxMin);
                    col = mix(vec3(0, 0, 0.5), vec3(0, 0.6, 1), ratio);

                    // float coord = vPos.z * 1.;
                    // float grid = abs(fract(coord - 0.5) - 0.5) / fwidth(coord) / lineThickness;
                    // float line = min(grid, 1.0);
                    // vec3 lineCol = mix(vec3(1, 1, 0),vec3(0, 1, 1), col.z);
                    // col = mix(lineCol, col, line);

                    gl_FragColor = vec4( col, opacity);
                `
                );
                //console.log(shader.fragmentShader);
            }
        });
        
            waterMesh.material = m;

            if (arrowHelperGroup) {
                scene.remove(arrowHelperGroup);
            }

            arrowHelperGroup = new THREE.Group();

            for (let i = 0; i < waterMesh.geometry.attributes.position.count; i += 100) { // 可以减少箭头数量以提高性能
                const pos = new THREE.Vector3(
                    waterMesh.geometry.attributes.position.getX(i),
                    waterMesh.geometry.attributes.position.getY(i),
                    waterMesh.geometry.attributes.position.getZ(i)
                );

                const normal = new THREE.Vector3(
                    waterMesh.geometry.attributes.normal.getX(i),
                    waterMesh.geometry.attributes.normal.getY(i),
                    0
                );

                // 创建箭头辅助器
                const arrowHelper = new THREE.ArrowHelper(normal.clone().normalize(), pos, 4, 0xff0000, 2, 1);

                arrowHelperGroup.add(arrowHelper);
            }

            arrowHelperGroup.rotateX(Math.PI * -0.5);
            scene.add(arrowHelperGroup);
            arrowHelperGroup.visible = !showTerrain;
        }

        function updateTerrain() {
            for (let i = 0, j = 0; i < waterTiffData.length; i++, j += 3) {
                const value = waterTiffData[i] * -1; // / 65535 * exaggerationScale * (-1.0);
                terrainMesh.geometry.attributes.position.array[j + 2] = value + 20;
                terrainMesh.geometry.attributes.position.needsUpdate = true;
            }
            terrainMesh.geometry.computeVertexNormals();
        }

        function updateBottom() {
            for (let i = 0, j = 0; i < waterTiffData.length; i++, j += 3) {
                const value = waterTiffData[i] * -1; // / 65535 * exaggerationScale * (-1.0);
                bottomMesh.geometry.attributes.position.array[j + 2] = value - 5;
                bottomMesh.geometry.attributes.position.needsUpdate = true;
            }
            bottomMesh.geometry.computeVertexNormals();
        }

        function update() {
            exaggerationScale = document.getElementById('haha').value;
            updateWater();
            updateTerrain();
            updateBottom();
            updateNewSide();
            updateNewSide2();
            updateSide();
            updateSide2();
        }

        function changeDisplayMode() {
            showTerrain = !showTerrain;

            //waterMesh.visible = !waterMesh.visible;
            if (terrainMesh) {
                terrainMesh.visible = showTerrain;
            }

            if (sideMesh) {
                sideMesh.visible = showTerrain;
            }

            if (arrowHelperGroup) {
                arrowHelperGroup.visible = !showTerrain;
            }

            if (showTerrain) {
                textLabels.forEach(text => {
                    text.visible = false;
                });
            }

            sideMeshes.forEach(mesh => {
                mesh.visible = showTerrain;
            });

            contoursLines.forEach(mesh => {
                mesh.visible = !showTerrain;
            });
        }

        function createTerrain() {
            const geometry = new THREE.PlaneGeometry(width, height, width - 1, height - 1);
            //console.log(geometry.attributes.position);
            for (let i = 0, j = 0; i < waterTiffData.length; i++, j += 3) {
                const value = waterTiffData[i] * -1; // / 65535 * exaggerationScale * (-1.0);
                geometry.attributes.position.array[j + 2] = value + 20;
                //console.log(data[i] / 65535 * 10000);
            }
            geometry.computeVertexNormals();

            const textureLoader = new THREE.TextureLoader();
            const texture = textureLoader.load('./assets/GeoTexture/草地.jpg');
            texture.wrapS = THREE.RepeatWrapping;
            texture.wrapT = THREE.RepeatWrapping;
            texture.repeat.set(20, 20);

            const material = new THREE.MeshBasicMaterial({ map: texture });

            terrainMesh = new THREE.Mesh(geometry, material);
            terrainMesh.visible = showTerrain;
            terrainMesh.rotateX(Math.PI * -0.5);
            scene.add(terrainMesh);
        }

        function createBottom() {
            const geometry = new THREE.PlaneGeometry(width, height, width - 1, height - 1);
            //console.log(geometry.attributes.position);
            for (let i = 0, j = 0; i < waterTiffData.length; i++, j += 3) {
                const value = waterTiffData[i] * -1; // / 65535 * exaggerationScale * (-1.0);
                geometry.attributes.position.array[j + 2] = value - 5;
                //console.log(data[i] / 65535 * 10000);
            }
            geometry.computeVertexNormals();

            const textureLoader = new THREE.TextureLoader();
            const texture = textureLoader.load('./assets/GeoTexture/6.jpg');
            texture.wrapS = THREE.RepeatWrapping;
            texture.wrapT = THREE.RepeatWrapping;
            texture.repeat.set(20, 20);

            const material = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide });

            bottomMesh = new THREE.Mesh(geometry, material);
            bottomMesh.rotateX(Math.PI * -0.5);
            scene.add(bottomMesh);

            return geometry;
        }

        function createContour(data, width, height, min, max) {
            if (contoursLines.length > 0) {
                contoursLines.forEach(line => {
                    scene.remove(line);
                })
                contoursLines = [];
            }

            if (textLabels.length > 0) {
                textLabels.forEach(text => {
                    scene.remove(text);
                })
                textLabels = [];
            }

                // 使用d3-contour生成等高线
            const contours = d3.contours()
                .size([width, height])
                .thresholds(d3.range(min, max, 2))
                (data.flat());
            

            const material = new THREE.LineBasicMaterial( { color: 0xffffff, linewidth: 1 } );

            // 绘制等高线
            contours.forEach(contour => {   
                contour.coordinates.forEach(polygon => {
                    polygon.forEach((ring, i) => {
                        const shape = new THREE.Shape();
                        const points = ring.map(([x, y]) => new THREE.Vector3(x - width / 2.0, -(y - height / 2.0), contour.value + 0.3));
                        if (edges.length > 0 && (points.length > edges.length || Math.abs(points.length - edges.length) < 200)) {
                            return;
                        }
                        const geometry = new THREE.BufferGeometry().setFromPoints(points);
                
                        const mesh = new THREE.Line(geometry, material);
                        mesh.renderOrder = 999;
                        mesh.rotateX(Math.PI * -0.5);
                        mesh.visible = !showTerrain;
                        scene.add(mesh);
                        contoursLines.push(mesh);

                        const index = parseInt(points.length / 2);
                        createText(contour.value.toFixed(1), [new THREE.Vector3(points[index].x, points[index].y, points[index].z)]);
                    });
                });
            });
        }

        function createText(value, points) {
            const canvas = document.createElement('canvas');
            canvas.width = 72;
            canvas.height = 72;
            const ctx = canvas.getContext("2d");
            ctx.rect(0, 0, 64, 64);
            ctx.fillStyle=`rgba(128,128,128,0)`;
            ctx.fill();

            ctx.fillStyle = `rgba(0, 0, 0, 1)`

            ctx.font = "24px Arial"
            ctx.fontweight = "bold"
            ctx.textBaseline = "middle";
            ctx.fillText(Math.abs(value).toString() + "m", 0, 16) 

            //console.log(canvas.toDataURL());

            const texture = new THREE.Texture(canvas);
            texture.needsUpdate = true;

            const geometry = new THREE.BufferGeometry().setFromPoints(points);
            const material = new THREE.PointsMaterial( { size: 72, sizeAttenuation: false, map: texture, transparent: true, depthTest: false } );
            const mesh = new THREE.Points(geometry, material);
            mesh.rotateX(Math.PI * -0.5);
            mesh.visible = !showTerrain;
            scene.add(mesh);
            textLabels.push(mesh);
        }
        
        // 窗口大小改变时更新渲染器和相机
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        // 动画循环
        function animate() {
            requestAnimationFrame(animate);

            // 更新OrbitControls
            controls.update();

            renderer.render(scene, camera);

            rotateImage();

            if (showTerrain) {
                textLabels.forEach(text => {
                    text.visible = false;   
                });
            } else {
                textLabels.forEach(text => {
                    if (camera.position.y - text.geometry.attributes.position.array[2] > 100) {
                        text.visible = false;
                    } else {
                        text.visible = true;
                    }
                });
            }
        }

        // 初始化
        init();
    </script>
</body>
</html>
